#pragma once

class CTelnetProtocolHandler
{
	enum NegState
	{
		NegState_Data,				// Reading data
		NegState_IAC,				// Have read an IAC
		NegState_Negotiating,		// Have read an IAC WILL/WONT/DO/DONT
		NegState_SubNegotiating,	// Have read an IAC SB
	};

	enum SubState
	{
		SubState_Option,			// Waiting to read the subnegotiation option
		SubState_Data,				// Reading parameters
		SubState_Option_IAC,		// Have read an IAC when waiting to read option
		SubState_Data_IAC			// Have read an IAC when reading data
	};

public:
	CTelnetProtocolHandler(void);
	~CTelnetProtocolHandler(void);

	void Reset();
	void Transpose(unsigned char* dataBuffer, int dataCount);
	int Negotiate(unsigned char* receiveBuffer, int receiveCount);

protected:
#pragma region ASCII constants
	// Interprete As Command
	static const unsigned char IAC             = 255;
	// [IAC] End Of Record
	static const unsigned char EOR             = 239;
	// [IAC] WILL
	static const unsigned char WILL            = 251;
	// [IAC] WONT
	static const unsigned char WONT            = 252;
	// [IAC] DO
	static const unsigned char DO              = 253;
	// [IAC] DONT
	static const unsigned char DONT            = 254;
	// [IAC] Sub Begin
	static const unsigned char SB              = 250;
	// [IAC] Sub End
	static const unsigned char SE              = 240;
	// Carriage Return
	static const unsigned char CR              = 13;
	// Line feed
	static const unsigned char LF              = 10;
	// NUL
	static const unsigned char NUL             = 0;
#pragma endregion

#pragma region Option constants
	/// <summary>
	/// Telnet option: binary mode
	/// </summary>
	static const unsigned char TELOPT_BINARY   = 0;
	/// <summary>
	/// Telnet option: echo on/off
	/// </summary>
	static const unsigned char TELOPT_ECHO     = 1;
	/// <summary>
	/// Telnet option: supress go ahead
	/// </summary>
	static const unsigned char TELOPT_SGA      = 3;
	/// <summary>
	/// Telnet option: End Of Record
	/// </summary>
	static const unsigned char TELOPT_EOR      = 25;
	/// <summary>
	/// Telnet option: Negotiate About Window Size
	/// </summary>
	static const unsigned char TELOPT_NAWS     = 31;
	/// <summary>
	/// Telnet option: Terminal Type
	/// </summary>
	static const unsigned char TELOPT_TTYPE    = 24;

	/// <summary>
	/// Telnet option qualifier 'IS'
	/// </summary>
	static const unsigned char TELQUAL_IS = 0;
	/// <summary>
	/// Telnet option qualifier 'SEND'
	/// </summary>
	static const unsigned char TELQUAL_SEND = 1;
#pragma endregion

	virtual void SetLocalEcho(bool echo);
    virtual void NotifyEndOfRecord();
	virtual void Write(unsigned char* dataBuffer, int dataCount) = 0;
	virtual void GetWindowSize(int& width, int& height) = 0;
	virtual const char* GetTerminalType() = 0;

	void SendReplyAndUpdateRecord(unsigned char request, unsigned char reply, unsigned char option);
	void SubNegotiationBegin(unsigned char option);
	void SubNegotiationAppendParameter(unsigned char parameter);
	void HandleSubNeg();
	bool IncreaseBufferIfFull(unsigned char* &buffer, int &size, int &allocatedSize);

	// Negotiation state variables
	NegState negState;
	SubState subState;
	unsigned char lastRequest;

	// Subnegotiation state variables
	unsigned char currentSubOption;
	unsigned char* subParameterBuffer;
	int allocatedSubParameterCount;
	int subParameterCount;

	// Buffer to store payload data
	unsigned char* payloadBuffer;
	int payloadCount;
	int allocatedPayloadCount;

	// What IAC DO(NT) request do we have received already ?
	unsigned char receivedDX[256];
	// What IAC WILL/WONT request do we have received already ?
	unsigned char receivedWX[256];
	// What IAC DO/DONT request do we have sent already ?
	unsigned char sentDX[256];
	// What IAC WILL/WONT request do we have sent already ?
	unsigned char sentWX[256];
};

